/*
	Copyright (c) 2008 TPSi
	Alex Iskander
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

*/

#include "BindableObject.h"
#include "Bindable.h"
#include "Selector.h"
#include <iostream>
#include <algorithm>
using namespace Bind;

Bindable::Bindable()
{
	//initialize name
	this->name = "";
	
	//initialize parent
	this->parent = NULL;
	
	//initialize depth
	this->depth = -1; //not bound means no priority level
	
	//set bound to
	this->boundTo = NULL;
	
	//set selector
	this->bindFieldTo = NULL;
	this->autoDeleteBindTo = false;
	
	//set autodelete (no formatter, so false)
	this->autoDeleteFormatter = false;
	
	//set isUpdating
	this->isUpdating = false;
	
	//set isSetting
	this->isSetting = false;
}

void Bindable::set(Bindable &property, bool copy)
{
	if (isSetting)
		return;
	
	isSetting = true;
	
	/* Clear any existing binds */
	if (this->boundTo)
	{
		/* Do any unbind actions... */
		this->onUnbind(this->boundTo);
		
		/* Remove listener */
		this->boundTo->removeListener(this);
		
		/* Remove link */
		this->boundTo = NULL;
	}
	
	/* Set boundTo */
	this->boundTo = &property;
	
	/* Add Listener */
	this->boundTo->addListener(this);
	
	/* Copy selector if copy is enabled */
	if (copy && property.bindTo())
		this->bindTo(property.bindTo(), false);
	
		
	
	/* Call any bind actions */
	this->onBind(&property, copy);
	
	isSetting = false;
}

void Bindable::bake()
{
	/* If we are bound */
	if (this->boundTo)
	{
		// do unbind actions...
		this->onUnbind(this->boundTo);
		
		//remove listener
		this->boundTo->removeListener(this);
		
		//remove link
		this->boundTo = NULL;
	}
}

Bindable &Bindable::operator=(Bindable &property)
{
	this->set(property, true);
	return *this;
}

Bindable &Bindable::operator=(Bindable *property)
{
	this->set(*property, true);
	return *this;
}

void Bindable::setName(std::string name)
{
	this->name = name;
}

std::string Bindable::getName()
{
	return this->name;
}

void Bindable::setParent(BindableObject *parent)
{
	this->parent = parent;
}

BindableObject *Bindable::getParent() const
{
	return this->parent;
}

Selector *Bindable::bindTo(Selector *bindTo, bool autoDelete)
{
	this->bindFieldTo = bindTo;
	this->autoDeleteBindTo = autoDelete;
	
	if (this->parent && this->bindFieldTo)
	{
		Bindable *prop = this->bindFieldTo->get(this->parent);
		if (prop)
			this->set(*(prop));
	}
	

	return bindTo;
}

Selector *Bindable::bindTo()
{
	return this->bindFieldTo;
}


long Bindable::getDepth()
{
	return this->depth;
}

void Bindable::setDepth(long depth)
{
	this->depth = depth;
}

bool Bindable::isBindableObject()
{
	return false;
}

BindableObject *Bindable::getBindableObject()
{
	return NULL;
}


void Bindable::addListener(Bindable *property)
{
	//add the listener to the listeners array
	this->listeners.push_back(property);
	property->addListeningTo(this);
}

void Bindable::removeListener(Bindable *property, int max)
{
	//prepare a counter
	int deleted = 0;
	
	/* Find the listener */
	//prepare an iterator
	std::vector<Bindable *>::iterator it;
	
	//find the first item
	it = std::find(this->listeners.begin(), this->listeners.end(), property);
	
	//if the iterator is an item
	while (it != this->listeners.end())
	{
		//remove the item
		it = this->listeners.erase(it);
		
		property->removeListeningTo(this);
		
		//increment deleted
		deleted++;
		
		//check it
		if (max > 0 && deleted >= max) return;
		
		//find the next one
		it = std::find(it, this->listeners.end(), property);
	}
}

void Bindable::update()
{
	/* Loop through listeners */
	//get an iterator
	std::vector<Bindable *>::iterator it;
	
	//loop
	for (it = this->listeners.begin(); it != this->listeners.end(); it++)
	{
		//alert
		(*it)->updated(this);
	}
}

void Bindable::updated(Bindable *property)
{
	//if we are already updating, stop now (this also allows bi-direcitonal
	//binds)
	if (this->isUpdating) return;
	
	//mark that we are updating
	this->isUpdating = true;

	//sub-classes should implement code here
	
	//alert our responders
	this->update();	
	
	
	//mark that we are done updating.
	this->isUpdating = false;
}

void Bindable::destroyed(Bindable *property)
{
	/* If we are bound to, unbind */
	if (this->boundTo == property)
	{
		//tell subclasses to disconnect
		this->onUnbind(property);
		
		//unbind
		this->boundTo = NULL;
	}
	
	/* If we are listening to, remove records of such nonesense */
	std::vector<Bindable *>::iterator it;
	it = std::find(listeningTo.begin(), listeningTo.end(), property);
	while (it != listeningTo.end())
	{
		it = listeningTo.erase(it);
		
		it = std::find(listeningTo.begin(), listeningTo.end(), property);
	}
	
}

void Bindable::onDestroy(DestructionListener *listener)
{
	//add the listener
	this->destructionListeners.insert(listener);
}

void Bindable::cancelOnDestroy(DestructionListener *listener)
{
	//remove the listener
	if (this->destructionListeners.count(listener) > 0)
	{
		this->destructionListeners.erase(listener);
	}
}

void Bindable::onBind(Bindable *property, bool copy)
{
	//sub-classes will implement
}

void Bindable::onUnbind(Bindable *property)
{
	//subclasses will implement
}

void Bindable::addListeningTo(Bindable *property)
{
	this->listeningTo.push_back(property);
}

void Bindable::removeListeningTo(Bindable *property)
{
	std::vector<Bindable *>::iterator it;
	it = std::find(this->listeningTo.begin(), this->listeningTo.end(), property);
	
	if (it != this->listeningTo.end())
	{
		this->listeningTo.erase(it);
	}
}

int Bindable::getResponderCount()
{
	return (int) this->listeners.size();
}

void Bindable::inheritable(bool allow)
{
	this->_inheritable = allow;
}

Bindable::~Bindable()
{
	/* Loop through destruction listeners */
	std::set<DestructionListener *>::iterator destructionListener;
	for (
		 destructionListener = this->destructionListeners.begin();
		 destructionListener != this->destructionListeners.end();
		 destructionListener++
		 )
	{
		(*destructionListener)->destroyed(this);
	}
	
	/* Loop through listeners */
	//get an iterator
	std::vector<Bindable *>::iterator it;
	
	//loop
	for (size_t i = 0; i < this->listeners.size(); i++)
	{
		//alert listener that we have been destroyed
		this->listeners[i]->destroyed(this);
	}
	
	//get iterator
	it = this->listeningTo.begin();

	//loop through listening to
	while(it != this->listeningTo.end())
	{
		//deregister
		(*it)->removeListener(this);
		
		//move back to beginning
		it = this->listeningTo.begin();
	}

	//if we should destroy the field we are bound to, do so.
	if (autoDeleteBindTo && bindFieldTo) delete bindFieldTo;
	
	//set our field to bind to to NULL for safety.
	bindFieldTo = NULL;
	
	
}